package com.alogic.html.xscript;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;

import com.alogic.html.util.HtmlTag;
import com.alogic.html.util.HtmlTagManager;
import com.alogic.xscript.AbstractLogiclet;
import com.alogic.xscript.ExecuteWatcher;
import com.alogic.xscript.Logiclet;
import com.alogic.xscript.LogicletContext;
import com.alogic.xscript.doc.XsObject;
import com.alogic.xscript.plugins.Segment;
import com.anysoft.util.BaseException;
import com.anysoft.util.IOTools;
import com.anysoft.util.Pair;
import com.anysoft.util.Properties;
import com.anysoft.util.PropertiesConstants;


/**
 * 配置文件
 * @author congzihan
 * @since 1.6.12.27 [20190327 duanyy]
 * 
 * @version 1.6.12.36 [20190611 duanyy] <br>
 * - 重构html文档框架<br>
 */
public class HTML extends Segment {

	protected String cid = "$h-conf";
	protected Pattern pattern = Pattern.compile("h-(\\w+)");
	public HTML(String tag, Logiclet p) {
		super(tag, p);
		
		registerModule("h-html",HTMLDoc.class);
		registerModule("h-head",HTMLDoc.Head.class);
		registerModule("h-title",HTMLDoc.Title.class);
		registerModule("h-h",HTMLDoc.Heading.class);
		registerModule("h-hr",HTMLDoc.Hr.class);
		registerModule("h-br",HTMLDoc.Br.class);
		
		registerModule("h-node",HTMLSimple.class);
		registerModule("h-raw",HTMLRaw.class);
		
		registerModule("h-save",HTMLSave.class);
		registerModule("h-md5",HTMLMd5.class);
		registerModule("h-content",HTMLContent.class);
	}	
	
	@Override
	protected Logiclet onCreateLogiclet(String xml, Logiclet p) {
		Logiclet logiclet = super.onCreateLogiclet(xml, p);
		if (logiclet == null){
			Matcher matcher = pattern.matcher(xml);
			if (matcher.find()){
				String htmlTag = matcher.group(1);
				if (StringUtils.isNotEmpty(htmlTag)){
					return new HTMLCommon(xml,p,htmlTag);
				}
			}
		}
		
		return logiclet;
	}	
	
	@Override
	public void configure(Properties p){
		super.configure(p);
		
		cid = PropertiesConstants.getString(p, "cid", cid,true);
	}
	
	@Override
	protected void onExecute(XsObject root, XsObject current, LogicletContext ctx, ExecuteWatcher watcher) {
		StringBuffer content = new StringBuffer();
		try {
			ctx.setObject(cid, content);
			super.onExecute(root, current, ctx, watcher);
		}finally{
			ctx.removeObject(cid);
		}
	}
	
	/**
	 * 子语句的父类
	 * @author duanwaiwai
	 *
	 */
	public abstract static class HTMLElement extends Segment{
		protected String pid = "$h-conf";
		
		public HTMLElement(String tag, Logiclet p) {
			super(tag,p);
		}
		
		@Override
		public void configure(Properties p){
			super.configure(p);
			pid = PropertiesConstants.getString(p, "pid", pid, true);
		}
		
	    @Override
	    protected void onExecute(XsObject root, XsObject current, final LogicletContext ctx, final ExecuteWatcher watcher) {
	        StringBuffer content = ctx.getObject(pid);
	        if (content == null) {
	            throw new BaseException("core.e1001", "It must be in a html context,check your script.");
	        }
	        onExecute(content,root,current,ctx,watcher);
	    }

	    protected abstract void onExecute(StringBuffer content, XsObject root, XsObject current, LogicletContext ctx,
				ExecuteWatcher watcher);
	    
		protected void onSuperExecute(XsObject root, XsObject current, LogicletContext ctx,
				ExecuteWatcher watcher) {
			super.onExecute(root, current, ctx, watcher);
		}
		
		public void appendElementAttr(StringBuffer buf,String name,String value){
			if (StringUtils.isNotEmpty(value) && StringUtils.isNotEmpty(name)){
				buf.append(String.format(" %s=\"%s\"", name,value));
			}
		}
	}
	
	/**
	 * 简单节点
	 * @author duanyy
	 *
	 */
	public static class HTMLSimple extends HTMLElement{

		protected String $text = "";
		protected String tag = "span";
		
		protected String $class = "";
		protected String $style = "";
		protected String $id = "";
		
		public HTMLSimple(String tag, Logiclet p) {
			super(tag, p);
		}
		
		@Override
		public void configure(Properties p){
			super.configure(p);
			$text = PropertiesConstants.getRaw(p, "text", "");
			$id = PropertiesConstants.getRaw(p, "id", $id);
			$class = PropertiesConstants.getRaw(p, "class", $class);
			$style = PropertiesConstants.getRaw(p, "style", $style);			
			tag = getHtmlTag(p,tag);
		}
		
		protected String getHtmlTag(Properties p,String dft){
			return PropertiesConstants.getString(p, "tag", dft);
		}
		
		@Override
		protected void onExecute(StringBuffer content, XsObject root, XsObject current, LogicletContext ctx,
				ExecuteWatcher watcher) {
	        String text = PropertiesConstants.transform(ctx, $text, "");

	        try {
	            content.append("\n<").append(tag);
	            appendElementAttr(content, "style", PropertiesConstants.transform(ctx, $style, ""));
	            appendElementAttr(content, "class", PropertiesConstants.transform(ctx, $class, ""));
	            appendElementAttr(content, "id", PropertiesConstants.transform(ctx, $id, ""));
	            content.append(">");

	            if (StringUtils.isNotEmpty(text)){
	            	content.append(text);
	            }else{
	            	super.onSuperExecute(root, current, ctx, watcher);
	            }
	        } finally {
	            content.append("</").append(tag).append(">");
	        }
		}		
	}
	
	/**
	 * 通用节点
	 * @author duanyy
	 *
	 */
	public static class HTMLCommon extends Segment{
		protected String pid = "$h-conf";
		protected String $text = "";
		protected String tag = "";
		protected boolean inline = true;
		protected List<Pair<String,String>> attrs = new ArrayList<Pair<String,String>>();
		
		public HTMLCommon(String tag, Logiclet p,String htmlTag) {
			super(tag, p);
			this.tag = htmlTag;
		}
		
		@Override
		public void configure(Properties p){
			super.configure(p);
			pid = PropertiesConstants.getString(p, "pid", pid, true);
			
			HtmlTagManager tagManager = HtmlTagManager.getDefault();
			
			HtmlTag htmlTag = tagManager.load(tag, true);		
			if (htmlTag != null){
				$text = PropertiesConstants.getRaw(p, htmlTag.getContentId(), "");
				for (String attr:htmlTag.getAttrs()){
					if (StringUtils.isNotEmpty(attr)){
						String value = PropertiesConstants.getRaw(p,attr,"");
						if (StringUtils.isNotEmpty(value)){
							attrs.add(new Pair.Default<String,String>(attr,value));
						}
					}
				}
				inline = htmlTag.inline();
			}
			
			String [] global = tagManager.getGlobalAttrs();
			if (global != null){
				for (String attr:global){
					if (StringUtils.isNotEmpty(attr)){
						String value = PropertiesConstants.getRaw(p,attr,"");
						if (StringUtils.isNotEmpty(value)){
							attrs.add(new Pair.Default<String,String>(attr,value));
						}
					}
				}
			}
			
			inline = PropertiesConstants.getBoolean(p, "inline", inline);
		}
		
	    @Override
	    protected void onExecute(XsObject root, XsObject current, final LogicletContext ctx, final ExecuteWatcher watcher) {
	        StringBuffer content = ctx.getObject(pid);
	        if (content == null) {
	            throw new BaseException("core.e1001", "It must be in a html context,check your script.");
	        }
	        onExecute(content,root,current,ctx,watcher);
	    }
	    
		protected void onSuperExecute(XsObject root, XsObject current, LogicletContext ctx,
				ExecuteWatcher watcher) {
			super.onExecute(root, current, ctx, watcher);
		}
		
		public void appendElementAttr(StringBuffer buf,String name,String value){
			if (StringUtils.isNotEmpty(value) && StringUtils.isNotEmpty(name)){
				buf.append(String.format(" %s=\"%s\"", name,value));
			}
		}		
		
		protected void onExecute(StringBuffer content, XsObject root, XsObject current, LogicletContext ctx,
				ExecuteWatcher watcher) {
	        String text = PropertiesConstants.transform(ctx, $text, "");

	        try {
	        	if (!inline){
	        		content.append("\n");
	        	}
	            content.append("<").append(tag);
	            
	            for (Pair<String,String> item:attrs){
	            	appendElementAttr(content, item.key(), PropertiesConstants.transform(ctx, item.value(), ""));
	            }
	            
	            content.append(">");

	            if (StringUtils.isNotEmpty(text)){
	            	content.append(text);
	            }else{
	            	super.onExecute(root, current, ctx, watcher);
	            }
	        } finally {
	            content.append("</").append(tag).append(">");
	        }
		}		
	}	
	
	/**
	 * 输出到变量
	 * 
	 * @author duanwaiwai
	 *
	 */
	public static class HTMLContent extends AbstractLogiclet {
		
		/**
		 * 上下文id
		 */
		protected String pid = "$h-conf";
		protected String $id = "$h-content";
		
		public HTMLContent(String tag, Logiclet p) {
			super(tag, p);
		}

		@Override
		public void configure(Properties p){
			super.configure(p);
			
			pid = PropertiesConstants.getString(p, "pid", pid,true);
			$id = PropertiesConstants.getRaw(p,"id",$id);
		}
		
		@Override
		protected void onExecute(XsObject root, XsObject current, final LogicletContext ctx, final ExecuteWatcher watcher){
			StringBuffer content = ctx.getObject(pid);
			if (content == null){
				throw new BaseException("core.e1001","It must be in a html context,check your script.");
			}
			
			String id = PropertiesConstants.transform(ctx, $id, "");
			if (StringUtils.isNotEmpty(id)){
				ctx.SetValue(id, content.toString());
			}
		}
	}
	
	/**
	 * 获取内容的md5码
	 * @author duanwaiwai
	 *
	 */
	public static class HTMLMd5 extends AbstractLogiclet {
		
		/**
		 * 上下文id
		 */
		protected String pid = "$h-conf";

		protected String id = "$h-md5";
		
		public HTMLMd5(String tag, Logiclet p) {
			super(tag, p);
		}

		@Override
		public void configure(Properties p){
			super.configure(p);
			
			pid = PropertiesConstants.getString(p, "pid", pid,true);
			id = PropertiesConstants.getRaw(p,"id",id);
		}
		
		@Override
		protected void onExecute(XsObject root, XsObject current, final LogicletContext ctx, final ExecuteWatcher watcher){
			StringBuffer content = ctx.getObject(pid);
			if (content == null){
				throw new BaseException("core.e1001","It must be in a html context,check your script.");
			}

			ctx.SetValue(id, DigestUtils.md5Hex(content.toString()));
		}
	}
	
	/**
	 * 输出到文件
	 * @author duanwaiwai
	 *
	 */
	public static class HTMLSave extends AbstractLogiclet {
	    /**
	     * 上下文id
	     */
	    protected String pid = "$h-conf";

	    protected String $path = "";

	    protected String encoding = "utf-8";

	    public HTMLSave(String tag, Logiclet p) {
	        super(tag, p);
	    }

	    @Override
	    public void configure(Properties p) {
	        super.configure(p);

	        pid = PropertiesConstants.getString(p, "pid", pid, true);
	        $path = PropertiesConstants.getRaw(p, "path", $path);
	        encoding = PropertiesConstants.getString(p, "encoding", encoding, true);
	    }

	    @Override
	    protected void onExecute(XsObject root, XsObject current, final LogicletContext ctx, final ExecuteWatcher watcher) {
	        StringBuffer content = ctx.getObject(pid);
	        if (content == null) {
	            throw new BaseException("core.e1001", "It must be in a html-conf context,check your script.");
	        }

	        String path = PropertiesConstants.transform(ctx, $path, "");
	        if (StringUtils.isNotEmpty(path)) {
	            File file = new File(path);
	            if (!file.exists()) {
	                FileOutputStream out = null;
	                try {
	                	String html = content.toString();
	                    out = new FileOutputStream(file);
	                    out.write(html.getBytes(encoding));
	                } catch (Exception ex) {
	                    log(ExceptionUtils.getStackTrace(ex), "error");
	                } finally {
	                    IOTools.close(out);
	                }
	            }
	        } else {
	            log(content.toString(), "info");
	        }
	    }

	}
}
